using ED.Common.Expressions.Visit;
using ED.Common.Extensions;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace ED.Common.Expressions.Base
{
    internal class BaseVisit
    {
        /// <summary>
        /// 生成当前节点的字符串描述
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual string GenerateFullMarkString(ExpressionNode node) => throw new NotImplementedException();
        /// <summary>
        /// 准备树形结构
        /// </summary>
        /// <param name="node"></param>
        /// <exception cref="NotImplementedException"></exception>
        protected virtual void Prepare(ExpressionNode node) => throw new NotImplementedException();
        /// <summary>
        /// 重新生成当前节点(当子节点有简化动作时,父节点要冒泡一样一级级跟着Rebuild)
        /// </summary>
        /// <param name="node"></param>
        /// <remarks>注意：简化后不能更改当前节点的类型,比如: [bool? ok;p=>ok??p.Id>1;] 整体是book,Left是bool?, 简化后虽然使用了[ok]的值, 但整体类型应仍然是[bool]</remarks>
        /// <returns></returns>
        internal virtual Expression Rebuild(ExpressionNode node) => node.Expression;

        /// <summary>
        /// 简化表达式树入口
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visit"></param>
        /// <param name="isKeepCallBack">是否保留此表达式,比如: 在 根据lambda解析生成update语句的时候,期望保留 new Person{}</param>
        /// <param name="isDisableReduce">是否禁止精简,用在 ExpressionHelper.GetFullMarkString 中</param>
        internal void Reduce(ExpressionNode node, Action<ExpressionNode> visit, Func<Expression, bool> isKeepCallBack, bool isDisableReduce = false)
        {
            Prepare(node);
            node.Children.ForEach(x => visit(x));
            //node.FullMarkString = GenerateFullMarkString(node);
            if (!isDisableReduce)
            {
                var isKeep = false;
                if (isKeepCallBack != null) isKeep = isKeepCallBack(node.Expression);
                SimpleReduce(node, isKeep);
                Reduce2(node);
            }
            //延迟生成当前节点的描述,因为在简化中用不到当前节点的
            node.FullMarkString = ExpressionHelper.GetVisit(node.NodeType).GenerateFullMarkString(node);

        }
        /// <summary>
        /// 在SimpleReduce基础上再进行一次简化,处理短路的运算符,比如: lambda[p=>true||p.IsWrite] 会简化为 Constant[true]
        /// </summary>
        /// <param name="node"></param>
        protected virtual void Reduce2(ExpressionNode node) { }

        /// <summary>
        /// 通用节点简化方法,简化条件:
        /// <list type="bullet">
        /// <item>当前节点必须有两个或以上的子节点;</item>
        /// <item>子节点中必须同时存在含参数的节点和不含参数的节点;</item>
        /// </list>
        /// 这样, 不含参数的子节点将被简化, 如果所有子节点都不含参数, 那说明简化的时机还没到, 延迟到父节点进行判断
        /// </summary>
        /// <param name="node"></param>
        /// <param name="isKeep"></param>
        private void SimpleReduce(ExpressionNode node, bool isKeep)
        {
            //表示当前节点是否是断点,如果是的话要尽量在这里执行简化操作,否则将简化操作延迟到父级
            var isBreakPoint = false;
            if (isKeep)
            {
                //用户逻辑指定保留当前节点,那么在直接在此节点进行简化操作
                isBreakPoint = true;
                //将自身标记为 parameter 防止被上级简化
                node.IsParameter = true;
                node.FakeParameter = Expression.Parameter(typeof(object));
                node.Parameters.Add(node.FakeParameter);
            }
            else
            {
                if (node.Children.Count > 1)
                {
                    for (int i = 0; i < node.Children.Count - 1; i++)
                    {
                        if (node.Children[i].HasParameter ^ node.Children[i + 1].HasParameter)
                        {
                            //不相同 要进行简化
                            isBreakPoint = true;
                            break;
                        }
                    }

                }
            }
            if (isBreakPoint)
            {
                for (int i = 0; i < node.Children.Count; i++)
                {
                    var child = node.Children[i];
                    if (!child.HasParameter && child.Expression != null)
                    {
                        if (child.NodeType == ExpressionType.Lambda)
                        {
                            //lambda表达式无法在父节点上被简化,最多将它的body简化成常量
                            //因为 lambda 自身被简化变成委托,不能被后续探查,除非将 lambda 的父节点一起简化掉
                            //如: lambda = (IEnumerable<Person> i) => i.Count() > 0 && i.Where(p => p.Id > 100 && flag).Any(); //flag为true,则一点都不能简化,falg为false,则可以将 lambda 的 body 简化成 false
                            //如: lambda = (IEnumerable<Person> i) => i.Count() > 0 && i.Where(() => num > 10 && flag).Any(); //可将 lambda 的 body 整体简化,但无法将 lambda 本身简化
                            //如: lambda = (IEnumerable<Person> i) => i.Count() > 0 && list.Where(p => p.Id > 100).Any(); //&&后的部分可以被简化为一个常量
                            //node.Children[i]._updateRequest = true;
                            continue;
                        }
                        else if (child.NodeType != ExpressionType.Constant)
                        {
                            //进行简化
                            var res = child.Reduce();
                            var para = Expression.Parameter(child.Expression.Type);
                            node.outMidValues.Add(para, res);
                            node.Children[i] = new ExpressionNode
                            {
                                Parent = node,
                                Expression = para,
                                _updateRequest = true,
                            };
                            node.Children[i].FullMarkString = node.Children[i].Expression.Type.GetClassFullName();
                        }
                    }
                }
            }
            if (node.NeedUpdate)
            {
                //如果子孙节点有更新 则 rebuid 当前节点
                node.Expression = Rebuild(node);
            }
        }

        /// <summary>
        /// 获取表达式树访问的参数属性名称
        /// </summary>
        /// <param name="node"></param>
        /// <param name="visit"></param>
        /// <param name="parameters"></param>
        /// <param name="names"></param>
        internal virtual void GetAccessName(ExpressionNode node, Action<ExpressionNode> visit, List<ParameterExpression> parameters, List<List<string>> names)
        {
            Prepare(node);
            node.Children.ForEach(x => visit(x));
            if (node.NodeType == ExpressionType.MemberAccess)
            {
                var member = node.Expression as MemberExpression;
                if (member.Expression.NodeType == ExpressionType.Parameter)
                {
                    var index = parameters.IndexOf(member.Expression as ParameterExpression);
                    if (index >= 0)
                    {
                        names[index].Add(member.Member.Name);
                    }
                }
            }
        }
    }
}
